////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   terrain_load.cpp
//  Version:     v1.00
//  Created:     28/5/2001 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: terrain loading
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "terrain.h"
#include "terrain_sector.h"
#include "ObjMan.h"
#include "terrain_water.h"
#include "3dEngine.h"
#include "Vegetation.h"

CTerrain::CTerrain( const STerrainInfo & TerrainInfo )
{
	m_nLoadedSectors = 0;
	m_bOceanIsVisible = 0;
	m_fDistanceToSectorWithWater = 0;
//	m_nDiffTexIndexTableSize = 0;
//	m_nDiffTexTreeLevelOffset = 0;
//	ZeroStruct(m_hdrDiffTexInfo);
//	ZeroStruct(m_TerrainTextureLayer);
//	m_ucpDiffTexTmpBuffer = 0;
	m_pTerrainEf = 0;
	m_nSunLightMask = 0;
//	ZeroStruct(m_SSurfaceType); // staic
	m_pOcean = 0;
  m_eEndianOfTexture = eLittleEndian;

	// load default textures
	m_nWhiteTexId = GetRenderer()->EF_LoadTexture("EngineAssets/Textures/white.dds",FT_DONT_STREAM|FT_DONT_RESIZE)->GetTextureID();
	m_nBlackTexId = GetRenderer()->EF_LoadTexture("EngineAssets/Textures/black.dds",FT_DONT_STREAM|FT_DONT_RESIZE)->GetTextureID();

	// set params
	m_nUnitSize					= TerrainInfo.nUnitSize_InMeters;
	m_fInvUnitSize			= 1.f/TerrainInfo.nUnitSize_InMeters;
	m_nTerrainSize			= TerrainInfo.nHeightMapSize_InUnits*TerrainInfo.nUnitSize_InMeters;
	m_nSectorSize				= TerrainInfo.nSectorSize_InMeters;
#ifndef SEG_WORLD
	m_nSectorsTableSize = TerrainInfo.nSectorsTableSize_InSectors;
#endif
	m_fHeightmapZRatio	=	TerrainInfo.fHeightmapZRatio;
	m_fOceanWaterLevel  = TerrainInfo.fOceanWaterLevel;

	m_nUnitsToSectorBitShift = 0;
	while(m_nSectorSize>>m_nUnitsToSectorBitShift > m_nUnitSize)
		m_nUnitsToSectorBitShift++;

	// make flat 0 level heightmap
//	m_arrusHightMapData.Allocate(TerrainInfo.nHeightMapSize_InUnits+1);
//	m_arrHightMapRangeInfo.Allocate((TerrainInfo.nHeightMapSize_InUnits>>m_nRangeBitShift)+1);

#ifndef SEG_WORLD
	assert(m_nSectorsTableSize == m_nTerrainSize/m_nSectorSize);
#endif

	m_nBitShift=0;
	while((128>>m_nBitShift) != 128/CTerrain::GetHeightMapUnitSize())
		m_nBitShift++;

	m_nTerrainSizeDiv =(m_nTerrainSize>> m_nBitShift) - 1;

	m_lstSectors.reserve(512); // based on inspection in MemReplay


	m_pTerrainEf           = MakeSystemMaterialFromShader("Terrain");
	m_pImposterEf					 = MakeSystemMaterialFromShader("Common.Imposter");

//	memset(m_arrImposters,0,sizeof(m_arrImposters));
//	memset(m_arrImpostersTopBottom,0,sizeof(m_arrImpostersTopBottom));

	// create default segment
	ISegmentsManager *pSM = Get3DEngine()->m_pSegmentsManager;
	if (!pSM || !pSM->CreateSegments(this)) {
		float f = (float) GetTerrainSize();
		CreateSegment(Vec3(f, f, f));
	}
	InitHeightfieldPhysics(0);

	m_StoredModifications.SetTerrain(*this);

	assert(m_SSurfaceType[0].GetDataSize() < MAX_SURFACE_TYPES_COUNT * 1000);

  m_pTerrainUpdateDispatcher = new CTerrainUpdateDispatcher();
}

void CTerrain::CloseTerrainTextureFile(int nSID)
{
#ifdef SEG_WORLD
	if (nSID < 0) {
		int nEndSID = GetMaxSegmentsCount();
		for (nSID = 0; nSID < nEndSID; ++nSID) {
			AABB aabb;
			if (!GetSegmentBounds(nSID, aabb))
				continue;
			CloseTerrainTextureFile(nSID);
		}
		return;
	}
#endif

  m_arrBaseTexInfos[nSID].m_nDiffTexIndexTableSize = 0;

	for (int i = m_lstActiveTextureNodes.size() - 1; i >= 0; --i)
  {
		if (m_lstActiveTextureNodes[i]->m_nSID == nSID)
  {
			m_lstActiveTextureNodes[i]->UnloadNodeTexture(false);
			m_lstActiveTextureNodes.Delete(i);
		}
  }
	for (int i = m_lstActiveProcObjNodes.size() - 1; i >= 0; --i)
	{
		if (m_lstActiveProcObjNodes[i]->m_nSID == nSID)
  {
			m_lstActiveProcObjNodes[i]->RemoveProcObjects(false);
			m_lstActiveProcObjNodes.Delete(i);
		}
  }

	if(GetParentNode(nSID))
		GetParentNode(nSID)->UnloadNodeTexture(true);

  m_bOpenTerrainTextureFileNoLog = false;
}

CTerrain::~CTerrain()
{
  SAFE_DELETE(m_pOcean);
  SAFE_DELETE(m_pTerrainUpdateDispatcher);

  for(int nSID=0; nSID<m_SSurfaceType.Count(); nSID++)
    DeleteSegment(nSID, true);

	CTerrainNode::ResetStaticData();
}

void CTerrain::InitHeightfieldPhysics(int nSID)
{
  if(GetCVars()->e_VoxTer && (!GetCVars()->e_VoxTerHeightmapEditing || !m_bEditor))
    return;

	// for phys engine
	primitives::heightfield hf;
	hf.Basis.SetIdentity();
	hf.origin.zero();
	hf.step.x = hf.step.y = (float)CTerrain::GetHeightMapUnitSize();
	hf.size.x = hf.size.y = CTerrain::GetTerrainSize()/CTerrain::GetHeightMapUnitSize();
	hf.stride.set(hf.size.y+1,1);
	hf.heightscale = 1.0f;//m_fHeightmapZRatio;
	hf.typemask = hf.typehole = STYPE_HOLE;
	hf.heightmask = ~STYPE_BIT_MASK; // hf.heightmask does not appear to be used anywhere. What is it for? Should it be ~15 or ~127?
	
	hf.fpGetHeightCallback = GetHeightFromUnits_Callback;
	hf.fpGetSurfTypeCallback = GetSurfaceTypeFromUnits_Callback;

	int arrMatMapping[MAX_SURFACE_TYPES_COUNT];
	memset(arrMatMapping,0,sizeof(arrMatMapping));
	for(int i=0; i<MAX_SURFACE_TYPES_COUNT; i++)
		if(IMaterial * pMat = m_SSurfaceType[nSID][i].pLayerMat)
    {
      if(pMat->GetSubMtlCount()>2)
        pMat = pMat->GetSubMtl(2);
      arrMatMapping[i] = pMat->GetSurfaceTypeId();
    }

	// KLUDGE: The 3rd parameter in the following call (nMats) cannot be 128 because it gets assigned to an unsigned:7!
	IPhysicalEntity *pPhysTerrain = GetPhysicalWorld()->SetHeightfieldData(&hf, arrMatMapping, min(127, MAX_SURFACE_TYPES_COUNT));
	pe_params_foreign_data pfd;
	pfd.iForeignData = PHYS_FOREIGN_ID_TERRAIN;
	pPhysTerrain->SetParams(&pfd);
}

void CTerrain::SetMaterialMapping(int nSID)
{
	int arrMatMapping[MAX_SURFACE_TYPES_COUNT];
	memset(arrMatMapping,0,sizeof(arrMatMapping));
	for(int i=0; i<MAX_SURFACE_TYPES_COUNT; i++)
		if(IMaterial * pMat = m_SSurfaceType[nSID][i].pLayerMat)
    {
      if(pMat->GetSubMtlCount()>2)
        pMat = pMat->GetSubMtl(2);
      arrMatMapping[i] = pMat->GetSurfaceTypeId();
    }

	GetPhysicalWorld()->SetMaterialMapping(arrMatMapping, min(127, MAX_SURFACE_TYPES_COUNT));
}
/*
int __cdecl CTerrain__Cmp_CVegetationForLoading_Size(const void* v1, const void* v2)
{
  CVegetationForLoading * p1 = ((CVegetationForLoading*)v1);
  CVegetationForLoading * p2 = ((CVegetationForLoading*)v2);

	PodArray<StatInstGroup> & lstStaticTypes = CStatObj::GetObjManager()->m_lstStaticTypes;

  CStatObj * pStatObj1 = (p1->GetID()<lstStaticTypes.Count()) ? lstStaticTypes[p1->GetID()].GetStatObj() : 0;
  CStatObj * pStatObj2 = (p2->GetID()<lstStaticTypes.Count()) ? lstStaticTypes[p2->GetID()].GetStatObj() : 0;

  if(!pStatObj1)
    return 1;
  if(!pStatObj2)
    return -1;

  int nSecId1 = 0;
  {  // get pos
    Vec3 vCenter = Vec3(p1->GetX(),p1->GetY(),p1->GetZ()) + (pStatObj1->GetBoxMin()+pStatObj1->GetBoxMax())*0.5f*p1->GetScale();
    // get sector ids
    int x = (int)(((vCenter.x)/CTerrain::GetSectorSize()));
    int y = (int)(((vCenter.y)/CTerrain::GetSectorSize()));
    // get obj bbox
    Vec3 vBMin = pStatObj1->GetBoxMin()*p1->GetScale();
    Vec3 vBMax = pStatObj1->GetBoxMax()*p1->GetScale();
    // if outside of the map, or too big - register in sector (0,0)
//    if( x<0 || x>=CTerrain::GetSectorsTableSize() || y<0 || y>=CTerrain::GetSectorsTableSize() ||
  //    (vBMax.x - vBMin.x)>TERRAIN_SECTORS_MAX_OVERLAPPING*2 || (vBMax.y - vBMin.y)>TERRAIN_SECTORS_MAX_OVERLAPPING*2)
    //  x = y = 0;

    // get sector id
    nSecId1 = (x)*CTerrain::GetSectorsTableSize() + (y);
  }

  int nSecId2 = 0;
  {  // get pos
    Vec3 vCenter = Vec3(p2->GetX(),p2->GetY(),p2->GetZ()) + (pStatObj2->GetBoxMin()+pStatObj2->GetBoxMax())*0.5f*p2->GetScale();
    // get sector ids
    int x = (int)(((vCenter.x)/CTerrain::GetSectorSize()));
    int y = (int)(((vCenter.y)/CTerrain::GetSectorSize()));
    // get obj bbox
    Vec3 vBMin = pStatObj2->GetBoxMin()*p2->GetScale();
    Vec3 vBMax = pStatObj2->GetBoxMax()*p2->GetScale();
    // if outside of the map, or too big - register in sector (0,0)
//    if( x<0 || x>=CTerrain::GetSectorsTableSize() || y<0 || y>=CTerrain::GetSectorsTableSize() ||
  //    (vBMax.x - vBMin.x)>TERRAIN_SECTORS_MAX_OVERLAPPING*2 || (vBMax.y - vBMin.y)>TERRAIN_SECTORS_MAX_OVERLAPPING*2)
    //  x = y = 0;

    // get sector id
    nSecId2 = (x)*CTerrain::GetSectorsTableSize() + (y);
  }

  if(nSecId1 > nSecId2)
    return -1;
  if(nSecId1 < nSecId2)
    return 1;

  if(p1->GetScale()*pStatObj1->GetRadius() > p2->GetScale()*pStatObj2->GetRadius())
    return 1;
  if(p1->GetScale()*pStatObj1->GetRadius() < p2->GetScale()*pStatObj2->GetRadius())
    return -1;

  return 0;
}

int __cdecl CTerrain__Cmp_Int(const void* v1, const void* v2)
{
  if(*(uint32*)v1 > *(uint32*)v2)
    return 1;
  if(*(uint32*)v1 < *(uint32*)v2)
    return -1;

  return 0;
}

void CTerrain::LoadVegetationances()
{
  assert(this); if(!this) return;

  PrintMessage("Loading static object positions ...");


  for( int x=0; x<CTerrain::GetSectorsTableSize(); x++)
  for( int y=0; y<CTerrain::GetSectorsTableSize(); y++)
    assert(!m_arrSecInfoPyramid[nSID][0][x][y]->m_lstEntities[STATIC_OBJECTS].Count());


  // load static object positions list
  PodArray<CVegetationForLoading> static_objects;
  static_objects.Load(Get3DEngine()->GetLevelFilePath("objects.lst"), gEnv->pCryPak);

	// todo: sorting in not correct for hierarchical system
  qsort(static_objects.GetElements(), static_objects.Count(), 
    sizeof(static_objects[0]), CTerrain__Cmp_CVegetationForLoading_Size);

  // put objects into sectors depending on object position and fill lstUsedCGFs
  PodArray<CStatObj*> lstUsedCGFs;
  for(int i=0; i<static_objects.Count(); i++)
  {
    float x       = static_objects[i].GetX();
    float y       = static_objects[i].GetY();
    float z       = static_objects[i].GetZ()>0 ? static_objects[i].GetZ() : GetZApr(x,y);
    int  nId      = static_objects[i].GetID();
    uint8 ucBr    = static_objects[i].GetBrightness();
		uint8 ucAngle = static_objects[i].GetAngle();
    float fScale  = static_objects[i].GetScale();

    if( nId>=0 && nId<GetObjManager()->m_lstStaticTypes.Count() && 
        fScale>0 && 
        x>=0 && x<CTerrain::GetTerrainSize() && y>=0 && y<CTerrain::GetTerrainSize() &&
        GetObjManager()->m_lstStaticTypes[nId].GetStatObj() )
    {
      if(GetObjManager()->m_lstStaticTypes[nId].GetStatObj()->GetRadius()*fScale < GetCVars()->e_VegetationMinSize)
        continue; // skip creation of very small objects

      if(lstUsedCGFs.Find(GetObjManager()->m_lstStaticTypes[nId].GetStatObj())<0)
        lstUsedCGFs.Add(GetObjManager()->m_lstStaticTypes[nId].GetStatObj());

      CVegetation * pEnt = (CVegetation*)Get3DEngine()->CreateRenderNode( eERType_Vegetation );
      pEnt->m_fScale = fScale;
      pEnt->m_vPos = Vec3(x,y,z);
      pEnt->SetStatObjGroupId(nId);
      pEnt->m_ucBright = ucBr;
			pEnt->CalcBBox();
      pEnt->Physicalize( );
    }
  }

  // release not used CGF's
  int nGroupsReleased=0;
  for(int i=0; i<GetObjManager()->m_lstStaticTypes.Count(); i++)
  {
    CStatObj * pStatObj = GetObjManager()->m_lstStaticTypes[i].GetStatObj();
    if(pStatObj && lstUsedCGFs.Find(pStatObj)<0)
    {
      Get3DEngine()->ReleaseObject(pStatObj);
      GetObjManager()->m_lstStaticTypes[i].pStatObj = NULL;
      nGroupsReleased++;
    }
  }

	PrintMessagePlus(" %d objects created", static_objects.Count());
}

void CTerrain::CompileObjects()
{
  // set max view distance and sort by size
	for(int nTreeLevel=0; nTreeLevel<TERRAIN_NODE_TREE_DEPTH; nTreeLevel++)
	for( int x=0; x<m_arrSecInfoPyramid[nSID][nTreeLevel].GetSize(); x++)
	for( int y=0; y<m_arrSecInfoPyramid[nSID][nTreeLevel].GetSize(); y++)
    m_arrSecInfoPyramid[nSID][nTreeLevel][x][y]->CompileObjects(STATIC_OBJECTS);
}*/

void CTerrain::GetStreamingStatus(int & nLoadedSectors, int & nTotalSectors)
{
  nLoadedSectors = m_nLoadedSectors;
	nTotalSectors = 0;
	int nSegs = GetMaxSegmentsCount();
	for (int nSID = 0; nSID < nSegs; ++nSID) {
		if (!m_pParentNodes[nSID])
			continue;
		int nSectors = CTerrain::GetSectorsTableSize(nSID);
		nTotalSectors = nSectors * nSectors;
	}
}

bool CTerrain::m_bOpenTerrainTextureFileNoLog = false;

bool CTerrain::OpenTerrainTextureFile(SCommonFileHeader &hdrDiffTexHdr, STerrainTextureFileHeader & hdrDiffTexInfo, const char * szFileName, uint8 *  & ucpDiffTexTmpBuffer, int & nDiffTexIndexTableSize, int nSID)
{
  FUNCTION_PROFILER_3DENGINE;
	assert(nSID >= 0); //!!!mpeykov: handle SID -1 if necessary

	//bool bSW = Get3DEngine()->m_pSegmentsManager; //!!! used as indication Segmented World mode is on, to be removed
	//if (bSW) {
	//	if (hdrDiffTexInfo.nLayerCount)
	//		return false;

	//	strcpy(hdrDiffTexHdr.signature, "CRY");
	//	hdrDiffTexHdr.file_type = eTerrainTextureFile;
	//	hdrDiffTexHdr.flags = 0;
	//	hdrDiffTexHdr.version = FILEVERSION_TERRAIN_TEXTURE_FILE;

	//	hdrDiffTexInfo.nLayerCount = 2;
	//	hdrDiffTexInfo.dwFlags = 0;
	//	hdrDiffTexInfo.fBrMultiplier = 1.0f;

	//	m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[0].nSectorSizePixels = 256;
	//	m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[0].eTexFormat = eTF_DXT5;
	//	m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[0].nSectorSizeBytes = 0;

	//	m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[1].nSectorSizePixels = 256;
	//	m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[1].eTexFormat = eTF_DXT5;
	//	m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[1].nSectorSizeBytes = 0;

	//	return false;
	//}

	m_arrBaseTexInfos[nSID].m_nDiffTexIndexTableSize = 0;

  if(GetParentNode(nSID))
    GetParentNode(nSID)->UnloadNodeTexture(true);
  else
    return false;

	bool bNoLog = m_bOpenTerrainTextureFileNoLog;
  m_bOpenTerrainTextureFileNoLog = true;

	if(!bNoLog)
		PrintMessage("Opening %s ...", szFileName);

	// rbx open flags, x is a hint to not cache whole file in memory.
	FILE * fpDiffTexFile = gEnv->pCryPak->FOpen(Get3DEngine()->GetLevelFilePath(szFileName), "rbx");

	if(!fpDiffTexFile) 
	{
		if(!bNoLog)
			PrintMessage("Error opening terrain texture file: file not found (you might need to regenerate the surface texture)");
		return false;
	}

	if(!gEnv->pCryPak->FRead(&hdrDiffTexHdr, 1, fpDiffTexFile, false))
	{
		gEnv->pCryPak->FClose(fpDiffTexFile);
		fpDiffTexFile=0;
		CloseTerrainTextureFile(nSID);
		if(!bNoLog)
			PrintMessage("Error opening terrain texture file: header not found (file is broken)");
		return false;
	}

  m_eEndianOfTexture = (hdrDiffTexHdr.flags & SERIALIZATION_FLAG_BIG_ENDIAN) ? eBigEndian : eLittleEndian;

  SwapEndian(hdrDiffTexHdr, m_eEndianOfTexture);

	if(strcmp(hdrDiffTexHdr.signature,"CRY") || hdrDiffTexHdr.file_type != eTerrainTextureFile)
	{
		gEnv->pCryPak->FClose(fpDiffTexFile); fpDiffTexFile=0;
		CloseTerrainTextureFile(nSID);
		PrintMessage("Error opening terrain texture file: invalid signature");
		return false;
	}

	if(hdrDiffTexHdr.version != FILEVERSION_TERRAIN_TEXTURE_FILE)
	{
		gEnv->pCryPak->FClose(fpDiffTexFile);
		fpDiffTexFile=0;
		CloseTerrainTextureFile(nSID);
		if(!bNoLog)
			Error("Error opening terrain texture file: version error (you might need to regenerate the surface texture)");
		return false;
	}

	gEnv->pCryPak->FRead(&hdrDiffTexInfo, 1, fpDiffTexFile, m_eEndianOfTexture != GetPlatformEndian());

	memset(m_arrBaseTexInfos[nSID].m_TerrainTextureLayer,0,sizeof(m_arrBaseTexInfos[nSID].m_TerrainTextureLayer));

	// layers
	for(uint32 dwI=0;dwI<hdrDiffTexInfo.nLayerCount;++dwI)
	{
		assert(dwI<=2);		if(dwI>2)break;				// too many layers

		gEnv->pCryPak->FRead(&m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[dwI], 1, fpDiffTexFile, m_eEndianOfTexture != GetPlatformEndian());

    PrintMessage("  TerrainLayer %d: TexFormat: %s, SectorTextureSize: %dx%d, SectorTextureDataSizeBytes: %d", 
      dwI, GetRenderer()->GetTextureFormatName(m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[dwI].eTexFormat),
      m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[dwI].nSectorSizePixels, m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[dwI].nSectorSizePixels,
      m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[dwI].nSectorSizeBytes);
	}

  // detect invalid hdrDiffTexInfo.fBrMultiplier, this allows to avoid re-export of all Crysis1 levels
  bool bBrMultiplierIsValid = false;
  for(int i=1; i<16; i++)
    if(1.f/hdrDiffTexInfo.fBrMultiplier == (float)i)
      bBrMultiplierIsValid = true;

  if(!bBrMultiplierIsValid)
    hdrDiffTexInfo.fBrMultiplier = 1.f;

  // unlock all nodes
  for(int nTreeLevel=0; nTreeLevel<TERRAIN_NODE_TREE_DEPTH; nTreeLevel++)
  for( int x=0; x<m_arrSecInfoPyramid[nSID][nTreeLevel].GetSize(); x++)
  for( int y=0; y<m_arrSecInfoPyramid[nSID][nTreeLevel].GetSize(); y++)
  {
    m_arrSecInfoPyramid[nSID][nTreeLevel][x][y]->m_nEditorDiffuseTex = 0;
    m_arrSecInfoPyramid[nSID][nTreeLevel][x][y]->m_nNodeTextureOffset = -1;
		m_arrSecInfoPyramid[nSID][nTreeLevel][x][y]->m_bMergeNotAllowed = false;
  }

	// index block
	{
		std::vector<int16> IndexBlock;		// for explanation - see saving code

		uint16 wSize;
		gEnv->pCryPak->FRead(&wSize, 1, fpDiffTexFile, m_eEndianOfTexture != GetPlatformEndian());

		IndexBlock.resize(wSize);

		for(uint16 wI=0;wI<wSize;++wI)
			gEnv->pCryPak->FRead(&IndexBlock[wI], 1, fpDiffTexFile, m_eEndianOfTexture != GetPlatformEndian());

    PrintMessage("  RGB multiplier: %.f, Texture indices: %d", 1.f/hdrDiffTexInfo.fBrMultiplier, wSize);

		int16 * pIndices = &IndexBlock[0];
		int16 nElementsLeft = wSize;

    if(wSize>0 && pIndices[0]>=0)
    {
      GetParentNode(nSID)->AssignTextureFileOffset(pIndices, nElementsLeft);
		  assert(nElementsLeft == 0);
    }

		nDiffTexIndexTableSize = wSize*sizeof(uint16) + sizeof(uint16);

    if(!m_bEditor)
    {
      FRAME_PROFILER( "CTerrain::OpenTerrainTextureFile: ReleaseHoleNodes & UpdateTerrainNodes", GetSystem(), PROFILE_3DENGINE );

      int nNodesCounterBefore = CTerrainNode::m_nNodesCounter;
      GetParentNode(nSID)->ReleaseHoleNodes();
      PrintMessage("  %d out of %d nodes cleaned", nNodesCounterBefore-CTerrainNode::m_nNodesCounter, nNodesCounterBefore);

      if(Get3DEngine()->m_pObjectsTree[nSID])
        Get3DEngine()->m_pObjectsTree[nSID]->UpdateTerrainNodes();
    }
  }

	delete [] ucpDiffTexTmpBuffer;
	ucpDiffTexTmpBuffer = new uint8[m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[0].nSectorSizeBytes+m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[1].nSectorSizeBytes];

	gEnv->pCryPak->FClose(fpDiffTexFile);
	fpDiffTexFile=0;

	// init texture pools
	m_texCache[0].InitPool(ucpDiffTexTmpBuffer, m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[0].nSectorSizePixels, m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[0].eTexFormat);
	
#ifndef SEG_WORLD
	// mihailb: normal map is now always available, both with or without AO data.
	if(m_arrBaseTexInfos[nSID].m_hdrDiffTexInfo.dwFlags == TTFHF_AO_DATA_IS_VALID && GetCVars()->e_TerrainAo)
#endif
		m_texCache[1].InitPool(ucpDiffTexTmpBuffer, m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[1].nSectorSizePixels, m_arrBaseTexInfos[nSID].m_TerrainTextureLayer[1].eTexFormat);

	// Initialize terrain AO flag
	if(gEnv->pRenderer)
		gEnv->pRenderer->EnableTerrainAO(IsAmbientOcclusionEnabled());

	return true;
}
